<!DOCTYPE html PUBLIC "-//W4C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html>
  <head>
    <title>Google Maps</title>
    <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=false&amp;key=ABQIAAAAzrOhydvk6aOjdI3StFhy1xT2yXp_ZAY8_ufC3CFXhHIE1NvwkxSGSrVHCQ1Vv7tR5qbMOUwg8Ehe0Q type="text/javascript"></script>
    <script type="text/javascript" src="media/jquery.js"></script>
  </head>
  <body onunload="GUnload()">

    <!-- you can use tables or divs for the overall layout -->
    <table border=1>
      <tr>
        <td>
           <div id="map" style="width: 900px; height: 600px"></div>
        </td>
	<td width = 200 valign="top" style="text-decoration: underline; color: #4444ff;">
           <div id="side_bar"></div>
        </td>
      </tr>
    </table>

    <noscript><b>JavaScript must be enabled in order for you to use Google Maps.</b> 
      However, it seems JavaScript is either disabled or not supported by your browser. 
      To view Google Maps, enable JavaScript by changing your browser options, and then 
      try again.
    </noscript>


    <script type="text/javascript">
    //<![CDATA[


    if (GBrowserIsCompatible()) {

      var side_bar_html = "";
      var xmlDoc;               // The dests XML
      var routesXmlDoc;         // Holds result of AJAX query

      var routeMarkers = [];    // Markers created per landmark
      var destMarkers = [];     // Markers created per destination

      var markerToolTips = [];
      var markerDists = [];
      var avgSpeed = 16.00   // 16 km/h

      var newPathPoints = [];   // GLatLng's of the new path being plotted
      var newPath;              // New path's GPolyline
      var newXml;               // New path's XML file for submission

      var destTimes = [700, 730, 800, 830, 900, 930, 1000, 1030];
      var destTimeStrings = ["7:00 AM", "7:30 AM", "8:00 AM", "8:30 AM", "9:00 AM", "9:30 AM", "10:00 AM", "10:30 AM"];

      // Wrapper encapsulating browser differences for AJAX queries
      function GetXmlHttpObject() {
        if (window.XMLHttpRequest) {
          // code for IE7+, Firefox, Chrome, Opera, Safari
          return new XMLHttpRequest();
        }
        if (window.ActiveXObject) {
          // code for IE6, IE5
          return new ActiveXObject("Microsoft.XMLHTTP");
        }
        return null;
      }

      function parseXmlString(xmlString) {
        var xmlDoc;

        if (window.DOMParser) {
          var parser = new DOMParser();
          xmlDoc = parser.parseFromString(xmlString,"text/xml");
        } else {
          xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
          xmlDoc.async = "false";
          xmlDoc.loadXML(xmlString); 
        }

	return xmlDoc;
      }

      function getDestTimesHtml(excludeTimes) {
        var html_str;

        for (var i = 0; i < destTimes.length; i++) {
	  var skip = false;

	  for (var j = 0; j < excludeTimes.length; j++) {
	    if (destTimes[i] == excludeTimes[j]) {
	      skip = true;
	    }
	  }

	  if (skip == false) {
	    html_str += '<option value="' + destTimes[i]+ '">' + destTimeStrings[i] + '</option>';
	  }
	}

	return '<select id="dest-time"><option value="-1">New dest time</option>' + html_str + '</select>';
      }

      // With the routes merging, the points form part of multiple
      // routes. So, check if a marker for this point is already
      // present.
      function routeMarkerPresent(point) {
          for (var l = 0; l < routeMarkers.length; l++) {
              if (routeMarkers[l].getLatLng().equals(point)) {
                  return true;
              }
          }

          return false;
      }

      // Remove the markers belonging to the currently selected dest/time
      function clearCurrentRoutes() {
          map.clearOverlays();
	  routeMarkers = [];
      }

      // Called when new path being plotted joins an existing route marker
      function completePath(latlng) {
        var xpath = "/dest-routes/dest-route/landmark[@lat='" + latlng.lat() + "' and @lng='" + latlng.lng() + "']"; 
        var nodes = routesXmlDoc.evaluate(xpath, routesXmlDoc, null, XPathResult.ANY_TYPE, null);
	var result = nodes.iterateNext();
	var newatt;
	var landmark;

	newPath.insertVertex(newPath.getVertexCount(), latlng);

        // Prepare XML for the new path

	// Create the root element
        newXml = routesXmlDoc.createElement("dest-routes");
	newatt = routesXmlDoc.createAttribute("dest-id");
	newatt.nodeValue = routesXmlDoc.documentElement.getAttribute("dest-id");
	newXml.setAttributeNode(newatt);
	newatt = routesXmlDoc.createAttribute("dest-time");
	newatt.nodeValue = routesXmlDoc.documentElement.getAttribute("dest-time");
	newXml.setAttributeNode(newatt);

        // Add the set of landmarks from the 'merging point' till the end
	// to the XML, and to the new path's polyline (forms the 'second half')
	newXml.appendChild(routesXmlDoc.createElement("dest-route"));
	newXml.firstChild.appendChild(result.cloneNode(true));

        while (result && result.parentNode.lastChild != result) {

	  result = result.nextSibling;
	  while (result && result.nodeType != 1) {
	    result = result.nextSibling;
	  }

          if (result) {
	    var point = new GLatLng(result.getAttribute('lat'), result.getAttribute('lng'));
	    newPath.insertVertex(newPath.getVertexCount(), point);
	    newXml.firstChild.appendChild(result.cloneNode(true));
	  }
	}

	// Add the newly created landmarks to the new path's XML
	// Forms the 'first half' of the route

	var destNode = newXml.firstChild;
	var mergeNode = destNode.firstChild;

	for (var i = 0; i < newPathPoints.length; i++) {
	    landmark = routesXmlDoc.createElement("landmark");

            var lat = newPathPoints[i].getLatLng().lat();
	    var lng = newPathPoints[i].getLatLng().lng();

	    newatt = routesXmlDoc.createAttribute("landmark-id");
	    newatt.nodeValue = "";
	    landmark.setAttributeNode(newatt);

	    newatt = routesXmlDoc.createAttribute("lat");
	    newatt.nodeValue = lat;
	    landmark.setAttributeNode(newatt);

	    newatt = routesXmlDoc.createAttribute("lng");
	    newatt.nodeValue = lng;
	    landmark.setAttributeNode(newatt);

            destNode.insertBefore(landmark, mergeNode);
	}

        // Setup the tooltips for the newly added markers
        var dist = markerDists[latlng.lat() + " " + latlng.lng()];
        for (var i = newPathPoints.length - 1; i >= 0; i--) {
	  var point = newPathPoints[i].getLatLng();
	  var idx = point.lat() + " " + point.lng();

          GEvent.addListener(newPathPoints[i], "mouseover", function() {
	    var latlng = this.getLatLng();
	    var idx = latlng.lat() + " " + latlng.lng();
	    this.openInfoWindowHtml(" " + markerToolTips[idx]);
	  });

          GEvent.addListener(newPathPoints[i], "mouseout", function() {
	    this.closeInfoWindow();
	  });

	  if (i == newPathPoints.length - 1) {
	    dist += point.distanceFrom(latlng);
	  } else {
	    dist += point.distanceFrom(newPathPoints[i + 1].getLatLng());
	  }
          markerToolTips[idx] = helpString(routesXmlDoc.documentElement.getAttribute("dest-time"), dist);
	}

	var xmlString = (new XMLSerializer()).serializeToString(newXml);
	alert(xmlString);
	/*
	$.post('submit_route/', xmlString, function(data) { 
	  alert(data);
	});
	*/


        // Clear listeners for the map and the routeMarkers
        GEvent.clearListeners(map, "click");
        for (var i = 0; i < routeMarkers.length; i++) {
          GEvent.clearListeners(routeMarkers[i], "click");
	}
      }

      // Create a new landmark and marker as part of new path being plotted
      function addLandmark(overlay, latlng) {
        var icon = new GIcon(G_DEFAULT_ICON);
	icon.image = "http://labs.google.com/ridefinder/images/mm_20_blue.png";
        var marker = new GMarker(latlng, {icon: icon});
	newPathPoints.push(marker);
	map.addOverlay(marker);

	newPath.insertVertex(newPath.getVertexCount(), latlng);
      }

      // Reset the newly plotted route
      function clearRoute() {
        map.removeOverlay(newPath);

        newPath = new GPolyline([], "green");
	map.addOverlay(newPath);

	for (i = 0; i < newPathPoints.length; i++) {
	  map.removeOverlay(newPathPoints[i]);
	}

        newXml = "";
	newPathPoints = [];

        GEvent.addListener(map, "click", addLandmark);
        for (var i = 0; i < routeMarkers.length; i++) {
	  map.removeOverlay(routeMarkers[i]);
          GEvent.addListener(routeMarkers[i], "click", completePath);
	  map.addOverlay(routeMarkers[i]);
	}
      }

      // Submit the newly plotted route
      function submitRoute() {
	var xmlString = (new XMLSerializer()).serializeToString(newXml);
	
//	$.post('submit_route/', xmlString});

	alert(xmlString);
      }

      // Join a selected dest/time tree
      function joinRoute(dest_id, dest_time) {

        newPath = new GPolyline([], "green");
	map.addOverlay(newPath);

        GEvent.addListener(map, "click", addLandmark);

        for (var i = 0; i < routeMarkers.length; i++) {
	  map.removeOverlay(routeMarkers[i]);
          GEvent.addListener(routeMarkers[i], "click", completePath);
	  map.addOverlay(routeMarkers[i]);
	}

        // put the assembled side_bar_html contents into the side_bar div
	side_bar_html = "Click on the map to add markers and begin plotting your route.";
	side_bar_html += "Click on an existing marker to join an existing route.";
	side_bar_html += "<br><br>";
	side_bar_html += "<a href=javascript:clearRoute()>Reset route</a><br>";
	side_bar_html += "<a href=javascript:submitRoute()>Submit route</a>";
        document.getElementById("side_bar").innerHTML = side_bar_html;
      }

      function helpString(destTime, distToDest) {
	var str;
	var minsToDest =  distToDest/ 1000 / avgSpeed * 60;

	var startTime = Math.floor(destTime / 100) * 60 + (destTime % 100) - minsToDest;

	str = "Start time: ";
	str +=  Math.floor(startTime / 60) + ":" + Math.floor(startTime % 60);
	str += "<br>";
	str += "Time to dest: ";

	if (minsToDest > 60) {
	  str += Math.floor(minsToDest / 60) + "hrs, " + Math.ceil(minsToDest % 60) + "mins";
	} else {
	  str += Math.ceil(minsToDest % 60) + "mins";
	}

	str += "<br>";
	str += "Dist to dest: ";
	str += Math.floor(distToDest) / 1000 + "km";

	return str;
      }

      // Function called on AJAX query completion, render the received data
      function displayRoutes() {
        if (xmlhttp.readyState != 4) {
          return;
        }

        clearCurrentRoutes();
	//alert((new XMLSerializer()).serializeToString(xmlhttp.responseXML))

        routesXmlDoc = xmlhttp.responseXML;
	var dest_name = routesXmlDoc.documentElement.getAttribute("dest-name");
	var dest_time = routesXmlDoc.documentElement.getAttribute("dest-time");
	var dest_id = routesXmlDoc.documentElement.getAttribute("dest-id");
        var routes = routesXmlDoc.documentElement.getElementsByTagName("dest-route");
	var minlat = 0xffff, maxlat = -0xffff, minlng = 0xffff, maxlng = -0xffff;

        for (var i = 0; i < routes.length; i++) {
	  var landmarks = routes[i].getElementsByTagName("landmark");
	  var pts = [];

	  for (var j = 0; j < landmarks.length; j++) {
	    var lat = parseFloat(landmarks[j].getAttribute("lat"));
	    var lng = parseFloat(landmarks[j].getAttribute("lng"));

            if (lat < minlat) minlat = lat;
            if (lat > maxlat) maxlat = lat;
            if (lng < minlng) minlng = lng;
            if (lng > maxlng) maxlng = lng;

	    var point = new GLatLng(lat,lng);
	    pts[j] = point;

            if (!routeMarkerPresent(point)) {
	      var label = landmarks[i].getAttribute("landmark-name");

	      // Destination gets a different icon
	      if (j == landmarks.length - 1) {
	        var icon = new GIcon(G_DEFAULT_ICON);
		icon.image = "http://labs.google.com/ridefinder/images/mm_20_gray.png";
		var marker = new GMarker(point, {icon: icon});
	      } else {
	        var marker = new GMarker(point);
	      }

              GEvent.addListener(marker, "mouseover", function() {
	        var latlng = this.getLatLng();
		var idx = latlng.lat() + " " + latlng.lng();
	        this.openInfoWindowHtml(" " + markerToolTips[idx]);
	      });

              GEvent.addListener(marker, "mouseout", function() {
	        this.closeInfoWindow();
	      });

	      routeMarkers.push(marker);
	      map.addOverlay(marker);
	    }
	  }

          // Setup tooltips for the markers. This code repeats for segments
	  // shared b/w multiple paths, no big deal.
          var distToDest = 0;
	  for (var j = pts.length - 1; j >= 0 ; j--) {
	    var point = pts[j];
	    var idx = point.lat() + " " + point.lng();

	    markerDists[idx] = distToDest;
            if (j != (pts.length - 1)) {
	      distToDest += point.distanceFrom(pts[j + 1]);
	    }
            markerToolTips[idx] = helpString(dest_time, distToDest);
	  }

	  map.addOverlay(new GPolyline(pts));
        }

	map.setCenter(new GLatLng((maxlat + minlat) / 2, (maxlng + minlng) / 2), 13);

	join_route_string = "<br><br><a href=javascript:joinRoute(" + dest_id + "," + dest_time + ")> Join the " + dest_time + "hrs route to " + dest_name + "</a>";
	document.getElementById("side_bar").innerHTML = side_bar_html + join_route_string;

        for (var i = 0; i < destMarkers.length; i++) {
	  map.removeOverlay(destMarkers[i]);
	}
      }

      // Make an AJAX query for routes for the specified dest/time
      function getRoutes(dest_id,dest_time) {

        xmlhttp=GetXmlHttpObject();
	if (xmlhttp==null) {
          alert ("Your browser does not support AJAX!");
          return;
        }

        var url = "dest_routes_xml/" + dest_id + "/" + dest_time;
        xmlhttp.onreadystatechange = displayRoutes;
        xmlhttp.open("GET",url,true);
        xmlhttp.send(null);
      }

      // Display the times for the selected destination
      function displayTimes(latlng) {
        var dests = xmlDoc.documentElement.getElementsByTagName("destination");

        for (var i = 0; i < dests.length; i++) {
          // obtain the attribues of each marker
          var lat = parseFloat(dests[i].getAttribute("lat"));
          var lng = parseFloat(dests[i].getAttribute("lng"));

	  if (lat == latlng.lat() && lng == latlng.lng()) {
	    var destTimes = dests[i].getElementsByTagName("dest-time");
	    var excludeTimes = [];

	    side_bar_html = "<b>" + dests[i].getAttribute("dest-name") + "</b><br><br>";
	    side_bar_html += "Destination time(s):<br>";

            for (var j = 0; j < destTimes.length; j++) {
	      side_bar_html += '<a href="javascript:getRoutes(' + parseInt(dests[i].getAttribute("dest-id")) + ',' + destTimes[j].firstChild.nodeValue + ')">' + 
		      destTimes[j].firstChild.nodeValue + 'hrs <\/a><br>';
              excludeTimes.push(parseInt(destTimes[j].firstChild.nodeValue));
	    }

            // Reuse the bit of code to add a new destination
	    // to add a new time to an existing destination.
	    newDestMarker = this;
	    newDestName = dests[i].getAttribute("dest-name");
	    newDestId = dests[i].getAttribute("dest-id");

	    side_bar_html += getDestTimesHtml(excludeTimes);
            side_bar_html += '<button type="button" onclick="addNewTime()">Proceed to plot a route</button><center>';
            side_bar_html += '<br><a href="javascript:addDest()>Add a new destination </a>';
	  }

	  document.getElementById("side_bar").innerHTML = side_bar_html;
	}
      }

      var newDestCreated;
      var newDestMarker;
      var newDestName;
      var newDestId;

      function addNewTime() {
        for (var i = 0; i < destMarkers.length; i++) {
	  if (destMarkers[i] != newDestMarker) {
	    map.removeOverlay(destMarkers[i]);
	  }
	}

        // It currently has an event to display dest times
	GEvent.clearListeners(newDestMarker, "click");
	plotFirstRoute();
      }

      function plotFirstRoute() {
        var dest_time = document.getElementById("dest-time").value;
	if (dest_time == "-1") {
	  alert("Please select a destination time");
	  return;
	}
	routeMarkers.push(newDestMarker);

        var point = newDestMarker.getLatLng();
	var idx = point.lat() + " " + point.lng();
	markerDists[idx] = 0;
	markerToolTips[idx] = helpString(dest_time, 0);

        GEvent.addListener(newDestMarker, "mouseover", function() {
	  var latlng = this.getLatLng();
	  var idx = latlng.lat() + " " + latlng.lng();
	  this.openInfoWindowHtml(" " + markerToolTips[idx]);
	});

        GEvent.addListener(newDestMarker, "mouseout", function() {
	  this.closeInfoWindow();
	});

        // Prepare a dummy route to the destination, containing just
        // the dest landmark. This facilitates creating the full paths xml
        // after plotting a route.
	var routesXmlString = '<?xml version="1.0"?> <dest-routes dest-id="' + newDestId + '" dest-time="' + dest_time + '" dest-name="' + newDestName + '"> <dest-route> <landmark landmark-id="' + newDestId + '" lat="' + point.lat() + '" lng="' + point.lng() + '" landmark-name=""/> </dest-route> </dest-routes>';

        routesXmlDoc = parseXmlString(routesXmlString);

        joinRoute(newDestId, dest_time);
      }

      function waitForDestAdd() {
	if (newDestCreated == false) {
          setTimeout(waitForDestAdd, 10);
	} else {
          GEvent.clearListeners(map, "click");
	}
      }

      function addDest() {

        // Disable events for other markers
        for (var i = 0; i < destMarkers.length; i++) {
	  GEvent.clearListeners(destMarkers[i], "click");
	}

	newDestCreated = false;
	alert("Click on the map to position the new destination");
        GEvent.addListener(map, "click", function (overlay, latlng) {
	  var excludeTimes = [];

          newDestName = prompt("Enter the name for the new destination");
	  newDestId = "TBD";

          // create the marker
	  var icon = new GIcon(G_DEFAULT_ICON);
	  icon.image = "http://labs.google.com/ridefinder/images/mm_20_gray.png";
	  newDestMarker = new GMarker(latlng, {icon: icon});

	  map.addOverlay(newDestMarker);
	  side_bar_html = '<br><br><center>' + getDestTimesHtml(excludeTimes) + '<br><br><button type="button" onclick="plotFirstRoute()">Proceed to plot a route</button><center>';
	  document.getElementById("side_bar").innerHTML = side_bar_html;

          // Remove the other destination markers
          for (var i = 0; i < destMarkers.length; i++) {
            map.removeOverlay(destMarkers[i]);
	  }
          newDestCreated = true;
        });
	
	waitForDestAdd();
      }

      // create the map
      var map = new GMap2(document.getElementById("map"));
      map.addControl(new GLargeMapControl());
      map.addControl(new GMapTypeControl());
      map.setCenter(new GLatLng(12.966667, 77.566667), 12);

      GDownloadUrl("dests_xml", function(doc) {
        xmlDoc = GXml.parse(doc);
	//alert((new XMLSerializer()).serializeToString(xmlDoc));
        var markers = xmlDoc.documentElement.getElementsByTagName("destination");
          
        for (var i = 0; i < markers.length; i++) {

          var lat = parseFloat(markers[i].getAttribute("lat"));
          var lng = parseFloat(markers[i].getAttribute("lng"));
          var point = new GLatLng(lat,lng);
          var label = markers[i].getAttribute("dest-name");

          // create the marker
	  var icon = new GIcon(G_DEFAULT_ICON);
	  icon.image = "http://labs.google.com/ridefinder/images/mm_20_gray.png";
	  var marker = new GMarker(point, {icon: icon});
          GEvent.addListener(marker, "click", displayTimes);
	  destMarkers.push(marker);

          map.addOverlay(marker);
        }

        side_bar_html = 'Click on a marker to view the route timings <br><br>';
        side_bar_html += '<a href="javascript:addDest()>Add a new destination </a>';
	document.getElementById("side_bar").innerHTML = side_bar_html;
      });
    }

    else {
      alert("Sorry, the Google Maps API is not compatible with this browser");
    }

    // This Javascript is based on code provided by the
    // http://econym.org.uk/gmap/

    //]]>
    </script>
  </body>

</html>

